/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "task_worker_ffrt.h"
#include "ffrt/task.h"

namespace ImageKnifePro {

typedef struct {
    ffrt_function_header_t header;
    ffrt_function_t func;
    ffrt_function_t after_func;
    void* arg;
} CFunction;

static void ExecFunctionWrapper(void* t)
{
    CFunction* f = static_cast<CFunction*>(t);
    if (f->func) {
        f->func(f->arg);
    }
}

static void DestroyFunctionWrapper(void* t)
{
    CFunction* f = static_cast<CFunction*>(t);
    if (f->after_func) {
        f->after_func(f->arg);
    }
}

#define FFRT_STATIC_ASSERT(cond, msg) int x(int static_assertion_##msg[(cond) ? 1 : -1])
static inline ffrt_function_header_t* FunctionWrapper(const ffrt_function_t func,
    const ffrt_function_t after_func, void* arg, ffrt_function_kind_t kind_t = ffrt_function_kind_queue)
{
    FFRT_STATIC_ASSERT(sizeof(CFunction) <= ffrt_auto_managed_function_storage_size,
        size_of_function_must_be_less_than_ffrt_auto_managed_function_storage_size);
    CFunction* f = static_cast<CFunction*>(ffrt_alloc_auto_managed_function_storage_base(kind_t));
    f->header.exec = ExecFunctionWrapper;
    f->header.destroy = DestroyFunctionWrapper;
    f->func = func;
    f->after_func = after_func;
    f->arg = arg;
    return reinterpret_cast<ffrt_function_header_t*>(f);
}

void TaskWorkerFFRT::TaskHandleFFRT::CancelTask()
{
    if (canceled_) {
        return;
    }
    if (handle_ != nullptr) {
        if (!ffrt_queue_cancel(handle_) && onCancelSuccess != nullptr) {
            // 返回0，cancel成功
            onCancelSuccess();
        }
        canceled_ = true;
    }
}

TaskWorkerFFRT::TaskHandleFFRT::~TaskHandleFFRT()
{
    if (handle_ != nullptr) {
        ffrt_task_handle_destroy(handle_);
    }
}

TaskWorkerFFRT::TaskWorkerFFRT()
{
    CreateApiQueue();
    mainQueue_ = ffrt_get_main_queue();
}

void TaskWorkerFFRT::SetMaxConcurrency(uint64_t concurrency)
{
    if (taskQueue_ == nullptr) {
        CreateTaskQueue(concurrency);
    }
}

void TaskWorkerFFRT::CreateTaskQueue(uint64_t maxConcurrency)
{
    ffrt_queue_attr_t queue_attr;
    (void)ffrt_queue_attr_init(&queue_attr);
    ffrt_queue_attr_set_max_concurrency(&queue_attr, maxConcurrency);
    taskQueue_ = ffrt_queue_create(ffrt_queue_concurrent, "ImageKnifePro TaskQueue", &queue_attr);

    ffrt_queue_attr_destroy(&queue_attr);
}

void TaskWorkerFFRT::CreateApiQueue()
{
    const uint64_t defaultConcurrency = 4;

    ffrt_queue_attr_t queue_attr;
    (void)ffrt_queue_attr_init(&queue_attr);
    ffrt_queue_attr_set_max_concurrency(&queue_attr, defaultConcurrency);
    apiQueue_ = ffrt_queue_create(ffrt_queue_concurrent, "ImageKnifePro ApiQueue", &queue_attr);

    ffrt_queue_attr_destroy(&queue_attr);
}

void TaskWorkerFFRT::ExecuteCallback(void* arg)
{
    IMAGE_KNIFE_CHECK(arg == nullptr);
    FuncData* funcData = static_cast<FuncData*>(arg);

    if (funcData->execute != nullptr) {
        funcData->execute(funcData->data);
    }

    if (funcData->complete != nullptr) {
        funcData->execute = funcData->complete;
        ffrt_function_header_t* queueFunc_t = FunctionWrapper(MainThreadCallback, nullptr, funcData);
        ffrt_queue_submit(static_cast<TaskWorkerFFRT*>(TaskWorker::GetInstance())->mainQueue_, queueFunc_t, nullptr);
    } else {
        delete funcData;
    }
}

void TaskWorkerFFRT::TaskCallback(void* arg)
{
    IMAGE_KNIFE_CHECK(arg == nullptr);
    TaskData* taskData = static_cast<TaskData*>(arg);

    if (taskData->executeTask != nullptr) {
        taskData->executeTask(taskData->task);
    }

    if (taskData->completeTask != nullptr) {
        auto func = [](void* arg) {
            TaskData* taskData = static_cast<TaskData*>(arg);
            taskData->completeTask(taskData->task);
            delete taskData;
        };
        TaskWorker::GetInstance()->ToMainThread(func, taskData);
    } else {
        delete taskData;
    }
}

void TaskWorkerFFRT::MainThreadCallback(void* arg)
{
    IMAGE_KNIFE_CHECK(arg == nullptr);
    FuncData* funcData = static_cast<FuncData*>(arg);

    if (funcData->execute != nullptr) {
        funcData->execute(funcData->data);
    }

    delete funcData;
}

bool TaskWorkerFFRT::PushTask(AsyncFunc execute, AsyncFunc complete, void *data,
                              std::string taskInfo, ImageKnifeQOS qos)
{
    ffrt_task_attr_t attr;
    ffrt_task_attr_init(&attr);
    ffrt_task_attr_set_qos(&attr, static_cast<ffrt_qos_default_t>(qos));
    ffrt_task_attr_set_name(&attr, taskInfo.c_str());

    FuncData* funcData = new FuncData;
    funcData->execute = execute;
    funcData->complete = complete;
    funcData->data = data;

    ffrt_function_header_t* queueFunc_t = FunctionWrapper(ExecuteCallback, nullptr, funcData);
    ffrt_queue_submit(apiQueue_, queueFunc_t, &attr);
    ffrt_task_attr_destroy(&attr);

    return true;
}

bool TaskWorkerFFRT::PushTask(AsyncTask execute, AsyncTask complete,
                              std::shared_ptr<ImageKnifeTaskInternal> task, ImageKnifeQOS qos)
{
    if (taskQueue_ == nullptr) {
        CreateTaskQueue();
    }

    ffrt_task_attr_t attr;
    ffrt_task_attr_init(&attr);
    ffrt_task_attr_set_qos(&attr, static_cast<ffrt_qos_default_t>(qos));
    ffrt_task_attr_set_name(&attr, ImageKnifeTaskInternal::GetTaskInfo(task->GetImageRequestType(),
                                                                       task->GetImageKnifeRequestInternal()).c_str());

    TaskData* taskData = new TaskData;
    taskData->executeTask = execute;
    taskData->completeTask = complete;
    taskData->task = task;

    ffrt_function_header_t* queueFunc_t = FunctionWrapper(TaskCallback, nullptr, taskData);
    ffrt_queue_submit(taskQueue_, queueFunc_t, &attr);
    ffrt_task_attr_destroy(&attr);

    return true;
}

void TaskWorkerFFRT::ToMainThread(std::function<void(void*)> func, void* data)
{
    FuncData* funcData = new FuncData;
    funcData->execute = func;
    funcData->data = data;
    ffrt_function_header_t* queueFunc_t = FunctionWrapper(MainThreadCallback, nullptr, funcData);
    ffrt_queue_submit(mainQueue_, queueFunc_t, nullptr);
}

std::shared_ptr<TaskWorker::TaskHandle> TaskWorkerFFRT::ToMainThreadWithDelay(std::function<void(void*)> func,
                                                                              void* data, uint32_t milliSecond)
{
    FuncData* funcData = new FuncData;
    funcData->execute = func;
    funcData->data = data;
    ffrt_function_header_t* queueFuncType = FunctionWrapper(MainThreadCallback, nullptr, funcData);

    const int thousand = 1000;
    ffrt_task_attr_t attr;
    ffrt_task_attr_init(&attr);
    ffrt_task_attr_set_qos(&attr, ffrt_qos_user_initiated);
    ffrt_task_attr_set_delay(&attr, milliSecond * thousand);

    auto taskHandle = std::make_shared<TaskHandleFFRT>(ffrt_queue_submit_h(mainQueue_, queueFuncType, &attr));
    ffrt_task_attr_destroy(&attr);
    // 任务被取消需要手动释放FuncData
    taskHandle->onCancelSuccess = [funcData]() {delete funcData;};
    return taskHandle;
}

TaskWorkerFFRT::~TaskWorkerFFRT()
{
    ffrt_queue_destroy(mainQueue_);
    ffrt_queue_destroy(taskQueue_);
    ffrt_queue_destroy(apiQueue_);
}

}